package com.woniuxy.shiro.utils;


import com.woniuxy.entity.AdminMessage;
import com.woniuxy.entity.Permission;
import com.woniuxy.service.AdminMessageService;
import com.woniuxy.service.PermissionService;
import com.woniuxy.utils.JWTUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.List;

@Configuration
public class MyRealm extends AuthorizingRealm {

    @Autowired
    AdminMessageService adminMessageService;
    @Autowired
    PermissionService permissionService;
    //设置名称
    @Override
    public void setName(String name) {
        super.setName("myRealm");
    }

    // 这个需要注意下, 多域模式中, 每个域里面 请自己配置自己的token来源
    // 这里的token是来自 自定义的JWTToken
    // 必须重写此方法，不然 传的jwt, shiro却使用的是UsernamePasswordToken进行解析,
    // 会报一个错, 你token格式不对(JWT的值是3部分, UsernamePasswordToken只有1节....)
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof MyJsonWebToken;
    }
    /*
     * 授权信息
     * 在前后端不分离的情况下, 在html页面使用<shiro>|后端使用shiro的@checkRole,@checkPermission相关注解才能触发
     * 前后端分离的模式, 就直接用注解验证权限即可, 根据需求来
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("MyRealm.doGetAuthorizationInfo, 授权方法进入!!!");
        //这里获取token 不用token过滤器直接从redis里拿数据
        String userName = JWTUtil.getUserName(principalCollection.toString());
        //去数据库查出用户信息，含角色
        AdminMessage rbacManager = adminMessageService.selectOneByAccount(userName);
        //授权信息集合
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //1.如果需求是角色可以配置多个,则如下获取所有角色 并放入授权信息集合(模拟多角色查询,虽然目前是单的)
        /*String roles = String.valueOf(user.getRoleId());
        if (!"".equals(roles)&&roles!=null){
            String[] roles1 = roles.split(",");
            for (String role: roles1) {
                simpleAuthorizationInfo.addRole(role);
            }
        }*/
        //2.目前是单个角色则直接这样用
       simpleAuthorizationInfo.addRole(rbacManager.getRoleId()+"");
        //3.通过角色, 去查询中间表 找到所有的权限  获取tyep字段,a接口权限link细节信息(同第1步)
        List<Permission> forRole = permissionService.findForRole(rbacManager.getRoleId());
        //4.转成HashSet去重存放
        HashSet<String> permissions = new HashSet<>();
        for (Permission Perm : forRole) {
            if(Perm.getCode()!=null){
                permissions.add(Perm.getCode());
            }
        }
        //5.放入simpleAuthorizationInfo对象中
        simpleAuthorizationInfo.addStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }

    /*
    *身份认证方法
    * 需要再用户登录系统时触发
    * 该方法将是我们的主要方法，流程登录后，携带token与权限信息，发送到vue中
    * 在页面渲染，直接传入菜单和页面按钮显示权限
    */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("MyRealm.doGetAuthenticationInfo, 用户认证方法进入!"+authenticationToken);
        String myJWT = (String) authenticationToken.getCredentials();
        String userName = JWTUtil.getUserName(myJWT);
        if (userName == null){
            throw new AuthenticationException("token无效");
        }
        //通过用户名查出当前用户密码
        AdminMessage user = adminMessageService.selectOneByAccount(userName);
        if (user == null){
            throw new AuthenticationException("User不存在！");
        }
        //通过存入的myJWT与传入的账号密码进行验证
        if (JWTUtil.verify(myJWT,userName,user.getPassword())){
            return new SimpleAuthenticationInfo(myJWT,myJWT,this.getName());
        }else {
            throw new AuthenticationException("用户名或密码错误！");
        }
    }
}
